namespace Plugins.Engine
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using Plugins.Core;
    using Plugins.Core.Interfaces;
    using Plugins.Interfaces;
    using Plugins.Library;
using System.Collections.Generic;

    [Serializable]
    public class PluginManager : IPluginManager
    {
        private static object _sync = new object();

        private readonly IPluginHostManager _hostManager;
        private readonly Dictionary<Guid, IPluginConfiguration> _configuration;
        public event EventHandler<PluginConfigurationHostEventArgs> LoadConfiguration;
        public event EventHandler<PluginConfigurationHostEventArgs> UnloadConfiguration;

        public PluginManager()
        {
            this._hostManager = new PluginHostManager();
            this._configuration = new Dictionary<Guid, IPluginConfiguration>();
        }

        public int Count
        {
            get
            {
                lock (_sync)
                {
                    return this._hostManager.Count;
                }
            }
        }

        public IPluginHostManager HostManager
        {
            get
            {
                return this._hostManager;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public IPluginConfigurationHost LoadHost<T>(Guid hostId, string hostName, string fileName, bool isDefault)
        {
            if (string.IsNullOrEmpty(hostName) || string.IsNullOrEmpty(fileName))
                throw new ArgumentException("invalid host parameters");
            return PluginHostManager.LoadHost<T>(this._hostManager, hostId, hostName, fileName, isDefault);
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public void LoadServiceProvider<T>(IPluginConfigurationHost host) where T : class, IPluginServiceProvider<T>
        {
            PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, ">>> {0}", PluginTypeHelper.GetTypeName(typeof(T))));
            if (host != null)
            {
                if (LoadConfiguration != null)
                {
                    LoadConfiguration(this, new PluginConfigurationHostEventArgs(host));
                }
                PluginHostManager.RegisterServiceProvider<T>(this._hostManager);
            }
            PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "<<< {0}", PluginTypeHelper.GetTypeName(typeof(T))));
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public void UnloadServiceProvider<T>(IPluginConfigurationHost host) where T : class, IPluginServiceProvider<T>
        {
            if (host != null)
            {
                if (_configuration.ContainsKey(host.Id))
                {
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, ">>> {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                    PluginHostManager.UnregisterServiceProvider<T>(this._hostManager);
                    IPluginConfiguration configuration = PluginHostManager.UnloadHost<T>(this._hostManager, host);
                    if (configuration != null)
                    {
                        if (UnloadConfiguration != null)
                        {
                            UnloadConfiguration(this, new PluginConfigurationHostEventArgs(configuration));
                        }
                    }
                }
                PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "<<< {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                _configuration.Remove(host.Id);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public IPluginConfigurationHost LoadServiceProvider<T>(string hostId, string hostName, string fileName, bool isDefault) where T : class, IPluginServiceProvider<T>
        {
            Guid id = GetHostId(hostId);
            if (!_configuration.ContainsKey(id))
            {
                PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, ">>> {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                IPluginConfiguration configuration = PluginHostManager.LoadHost<T>(this._hostManager, id, hostName, fileName, isDefault);
                if (configuration != null)
                {
                    if (LoadConfiguration != null)
                    {
                        LoadConfiguration(this, new PluginConfigurationHostEventArgs(configuration));
                    }
                    PluginHostManager.RegisterServiceProvider<T>(this._hostManager);
                }
                PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "<<< {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                _configuration.Add(id, configuration);
                return configuration;
            }
            return _configuration[id];
        }

        private static Guid GetHostId(string hostId)
        {
            Guid id;
            try
            {
                id = new Guid(hostId);
            }
            catch (ArgumentNullException)
            {
                id = Guid.NewGuid();
            }
            catch (ArgumentException)
            {
                id = Guid.NewGuid();
            }

            return id;
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public IPluginConfiguration Load<T>(string hostId, string hostName, string fileName, bool isDefault)
        {
            Guid id = GetHostId(hostId);
            if (!_configuration.ContainsKey(id))
            {
                PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, ">>> {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                IPluginConfiguration configuration = PluginHostManager.LoadHost<T>(this._hostManager, id, hostName, fileName, isDefault);
                if (configuration != null)
                {
                    if (LoadConfiguration != null)
                    {
                        LoadConfiguration(this, new PluginConfigurationHostEventArgs(configuration));
                    }
                    PluginHostManager.RegisterObject<T>(this._hostManager, null);
                }
                PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "<<< {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                _configuration.Add(id, configuration);
                return configuration;
            }
            return _configuration[id];
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public void Unload<T>(IPluginConfiguration configuration)
        {
            if (configuration != null)
            {
                if (_configuration.ContainsKey(configuration.Id))
                {
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, ">>> {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                    if (PluginHostManager.UnloadHost<T>(this._hostManager, configuration) == configuration)
                    {
                        PluginHostManager.UnregisterObject<T>(this._hostManager, null);
                    }
                    if (UnloadConfiguration != null)
                    {
                        UnloadConfiguration(this, new PluginConfigurationHostEventArgs(configuration));
                    }
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "<<< {0}", PluginTypeHelper.GetTypeName(typeof(T))));
                    _configuration.Remove(configuration.Id);
                }
            }
        }

        public IPluginConfigurationHost this[Guid guid]
        {
            get 
            {
                if (_configuration.ContainsKey(guid))
                {
                    return _configuration[guid];
                }
                return null;
            }
        }
    }
}

